/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    fdcan.c
  * @brief   This file provides code for the configuration
  *          of the FDCAN instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "fdcan.h"

/* USER CODE BEGIN 0 */

#include "slcan.h"
#include "usbd_cdc_if.h"

static FDCAN_FilterTypeDef filter;
static uint32_t prescaler = 100;
static uint32_t clock_divider = FDCAN_CLOCK_DIV12;
static can_bus_state_t bus_state = OFF_BUS;
static uint8_t can_autoretransmit = ENABLE;
static can_txbuf_t txqueue = {0};

FDCAN_RxHeaderTypeDef rx_msg_header;
uint8_t rx_msg_data[8] = {0};
uint8_t msg_buf[SLCAN_MTU];
/* USER CODE END 0 */

FDCAN_HandleTypeDef hfdcan1;

/* FDCAN1 init function */
void MX_FDCAN1_Init(void)
{

  /* USER CODE BEGIN FDCAN1_Init 0 */

  /* USER CODE END FDCAN1_Init 0 */

  /* USER CODE BEGIN FDCAN1_Init 1 */

  /* USER CODE END FDCAN1_Init 1 */
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.ClockDivider = clock_divider;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = DISABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = DISABLE;
  hfdcan1.Init.NominalPrescaler = prescaler;
  hfdcan1.Init.NominalSyncJumpWidth = 1;
  hfdcan1.Init.NominalTimeSeg1 = 8;
  hfdcan1.Init.NominalTimeSeg2 = 3;
  hfdcan1.Init.DataPrescaler = prescaler;
  hfdcan1.Init.DataSyncJumpWidth = 1;
  hfdcan1.Init.DataTimeSeg1 = 8;
  hfdcan1.Init.DataTimeSeg2 = 3;
  hfdcan1.Init.StdFiltersNbr = 0;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN FDCAN1_Init 2 */
    HAL_StatusTypeDef status;
    FDCAN_FilterTypeDef sFilterConfig;
    sFilterConfig.IdType = FDCAN_STANDARD_ID;
    sFilterConfig.FilterIndex = 0;
    sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
    sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
    sFilterConfig.FilterID1 = 0x000;
    sFilterConfig.FilterID2 = 0x7FF;
    status =HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);
    if (status != HAL_OK){
        Error_Handler();
    }
//    sFilterConfig.IdType = FDCAN_EXTENDED_ID;
//    sFilterConfig.FilterIndex = 1;
//    sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
//    sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
//    sFilterConfig.FilterID1 = 0x00000000;
//    sFilterConfig.FilterID2 = 0x1FFFFFFF;
//    HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);
//    HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE);
//    if (status != HAL_OK){
//        Error_Handler();
//    }
    status = HAL_FDCAN_Start(&hfdcan1);
    if (status == HAL_OK){
//            status = HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_COMPLETE |
//                                                              FDCAN_IT_RX_FIFO0_NEW_MESSAGE |
//                                                              FDCAN_IT_RX_FIFO1_FULL |
//                                                              FDCAN_IT_TX_FIFO_EMPTY |
//                                                              FDCAN_IT_BUS_OFF, 0);


        if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
        {
            Error_Handler();
        }
        bus_state = ON_BUS;
    }
  /* USER CODE END FDCAN1_Init 2 */

}

void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(fdcanHandle->Instance==FDCAN1)
  {
  /* USER CODE BEGIN FDCAN1_MspInit 0 */

  /* USER CODE END FDCAN1_MspInit 0 */
  /** Initializes the peripherals clocks
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
    PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* FDCAN1 clock enable */
    __HAL_RCC_FDCAN_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**FDCAN1 GPIO Configuration
    PB8-BOOT0     ------> FDCAN1_RX
    PB9     ------> FDCAN1_TX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* FDCAN1 interrupt Init */
    HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
  /* USER CODE BEGIN FDCAN1_MspInit 1 */

  /* USER CODE END FDCAN1_MspInit 1 */
  }
}

void HAL_FDCAN_MspDeInit(FDCAN_HandleTypeDef* fdcanHandle)
{

  if(fdcanHandle->Instance==FDCAN1)
  {
  /* USER CODE BEGIN FDCAN1_MspDeInit 0 */

  /* USER CODE END FDCAN1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_FDCAN_CLK_DISABLE();

    /**FDCAN1 GPIO Configuration
    PB8-BOOT0     ------> FDCAN1_RX
    PB9     ------> FDCAN1_TX
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);

    /* FDCAN1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn);
  /* USER CODE BEGIN FDCAN1_MspDeInit 1 */

  /* USER CODE END FDCAN1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
void can_enable(void)
{
    if (bus_state == OFF_BUS)
    {
        MX_FDCAN1_Init();
    }
}

// Disable the CAN peripheral and go off-bus
void can_disable(void)
{
    if (bus_state == ON_BUS)
    {
        HAL_FDCAN_Stop(&hfdcan1);
        bus_state = OFF_BUS;
    }
}
// 设置CAN波特??
void can_set_bitrate(enum can_bitrate bitrate)
{
    if (bus_state == ON_BUS)
    {
        return;
    }

    switch (bitrate)
    {
        case CAN_BITRATE_10K:
            prescaler = 100;
            clock_divider = FDCAN_CLOCK_DIV12;
            break;
        case CAN_BITRATE_20K:
            prescaler = 50;
            clock_divider = FDCAN_CLOCK_DIV12;
            break;
        case CAN_BITRATE_50K:
            prescaler = 240;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_100K:
            prescaler = 120;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_125K:
            prescaler = 96;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_250K:
            prescaler = 48;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_500K:
            prescaler = 24;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_750K:
            prescaler = 16;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_1000K:
            prescaler = 12;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
        case CAN_BITRATE_INVALID:
        default:
            prescaler = 12;
            clock_divider = FDCAN_CLOCK_DIV1;
            break;
    }
}
void can_set_silent(uint8_t silent)
{
    if (bus_state == ON_BUS)
    {
        return;
    }
    //TODO
    if (silent)
    {
        //can_handle.Init.Mode = CAN_MODE_SILENT;
    } else {
        //an_handle.Init.Mode = CAN_MODE_NORMAL;
    }
}
void can_set_autoretransmit(uint8_t autoretransmit)
{
    if (bus_state == ON_BUS)
    {
        return;
    }
    if (autoretransmit)
    {
        can_autoretransmit = ENABLE;
    } else {
        can_autoretransmit = DISABLE;
    }
}
uint32_t can_tx(FDCAN_TxHeaderTypeDef *tx_msg_header, uint8_t* tx_msg_data)
{
    if( ((txqueue.head + 1) % TXQUEUE_LEN) == txqueue.tail)
    {
        // TODO: handle error
        return HAL_ERROR;
    }

    txqueue.header[txqueue.head] = *tx_msg_header;

    for(uint8_t i=0; i<tx_msg_header->DataLength; i++)
    {
        txqueue.data[txqueue.head][i] = tx_msg_data[i];
    }

    txqueue.head = (txqueue.head + 1) % TXQUEUE_LEN;

    return HAL_OK;
}

uint32_t can_rx(FDCAN_RxHeaderTypeDef *rx_msg_header, uint8_t* rx_msg_data)
{
    HAL_StatusTypeDef status = HAL_FDCAN_GetRxMessage(&hfdcan1,
                                                      FDCAN_RX_FIFO0,
                                                      rx_msg_header,
                                                      rx_msg_data);
    return status;
}

void can_process(void)
{
    if (can_rx(&rx_msg_header, rx_msg_data) == HAL_OK){
        // Parse received message
        int8_t msg_len = slcan_parse_frame((uint8_t *)&msg_buf, &rx_msg_header, rx_msg_data);

        // Transmit message via USB-CDC
        if(msg_len)
        {
            CDC_Transmit_FS(msg_buf, msg_len);
        }
    }
}

uint8_t is_can_msg_pending(uint8_t fifo)
{
    if (bus_state == OFF_BUS)
    {
        return 0;
    }
//    return(HAL_CAN_GetRxFifoFillLevel(&can_handle, CAN_RX_FIFO0) > 0);

    return 0;
}

FDCAN_HandleTypeDef*  can_gethandle(void)
{
    return &hfdcan1;
}

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
    {
        osSemaphoreRelease(sem_can_rx);
    }
}
/* USER CODE END 1 */
